package ir;

import ir.blocks.UnitFacetBlk;
import ir.blocks.UnitListsBlk;

import java.util.ArrayList;
import java.util.List;

import run.Bind;
import run.Facet;

/**
 * An IR cell representing "chains" of bindings.
 * @author Thomas Chau
 *
 */
public class Stmt {
	
	private ArrayList<Bind> bindings;
	private ArrayList<Object> block_bind_list; // unrefined list of blocks and binds to be later processed
	
	/**
	 * The general Statement: a chain of bindings.
	 * BLK_1 -> BLK_2
	 * Binds the right-operands of BLK_1 to the left-operands of BLK_2 
	 * 		e.g.
	 * 			CELLA(.a,.b) -> .c(CELLB)
	 * 		binds .a,.b to .c 
	 * @param list
	 */
	public Stmt(ArrayList<Object> list) {
		assert ((list.size() % 2) != 0);
		this.block_bind_list = list;
	}
	
	/**
	 * BLK -> BLK -> BLK -> BLK
	 * Returns the list of bindings represented.
	 * @return list of bindings represented by this statement
	 */
	public ArrayList<run.Bind> getBinds(StructuralDefinition d) {
		
			bindings = new ArrayList<Bind>();
			ArrayList<Object> list = block_bind_list;
			
			for (int i = 1; i < list.size() -1; i += 2) {
				try {
				// for each odd-numbered entry (a bind) add children, enter it
				ir.Block leftblock = (ir.Block) list.get(i - 1);
				List<Facet> left_operands = leftblock.getRightFacets(d);
				
				ir.Block rightblock = (ir.Block) list.get(i + 1);
				List<Facet> right_operands = rightblock.getLeftFacets(d);
				
				// now link them
				BindType bindType = (BindType) list.get(i);
				
				// localized, block-level bindings
				List<Bind> local_bindings = null;

					local_bindings = bindType.bind(left_operands, right_operands);

				
				// added to the whole statement's bindings total
				bindings.addAll(local_bindings);
				} catch (Exception e) {
					e.printStackTrace();
					while(true){}
				}
			}
			return bindings;
	}

	public CellUnit findInstance(String labelName) {
		for (Object o : block_bind_list) {
			if (o instanceof Block) {
				CellUnit candidate = ((Block) o).findInstance(labelName);
				if (candidate != null) 
					return candidate;
			}
		}
		return null;
	}
	
	public String toString() {
		String s = "";
		for (Object o : block_bind_list) {
			s += o.toString();
		}
		return s;
	}
	
	public void preFilter() {
		// preprocess the blocks
		for (Object o : block_bind_list) {
			if (o instanceof UnitListsBlk) {
				UnitListsBlk b = (UnitListsBlk) o;
				
				UnitFacetBlk unitfacet = b.xferUnitFacet();
				if (unitfacet != null) {
					block_bind_list.set(block_bind_list.indexOf(o), unitfacet);
				}
				
			}
		}	
	}
	
	public Stmt clone() {
		ArrayList<Object> innard = new ArrayList<Object>();
		for (Object o : block_bind_list) {
			if (o instanceof Block)
				innard.add(((Block)o).clone());
			else innard.add(o);
		}
		return new Stmt(innard);
	}
}
